package com.errol.batchprocessor.employeeaddition.advisorimpl.payroll;

import com.renjia.constant.Constant;
import com.renjia.dao.*;
import com.renjia.entity.BusPayrollRecord;
import com.renjia.entity.BusPayrollRecordDetail;
import com.renjia.entity.CoreEmployeeImportBatch;
import com.renjia.enums.PayrollDetailValidateStatusEnum;
import com.renjia.enums.PayrollValidateStatusEnum;
import com.renjia.enums.SwitchEnum;
import com.renjia.pojo.AutoCompleteFlagParser;
import com.renjia.properties.UkerServerProperties;
import com.renjia.service.CoreEmployeeImportBatchService;
import com.renjia.service.generic.log.LogService;
import com.renjia.service.generic.middleware.batchprocessor.payload.AbstractBatchPayload;
import com.renjia.service.generic.support.customer.CustomerChannelSupportService;
import com.renjia.service.generic.support.employee.BankCardSupportService;
import com.renjia.service.generic.support.employee.EmployeeCustomerSupportService;
import com.renjia.service.generic.support.employee.EmployeeSupportService;
import com.renjia.service.generic.support.payroll.PayrollDetailSupportService;
import com.renjia.service.generic.support.payroll.PayrollRecordSupportService;
import com.renjia.service.integration.threeelements.ThreeElementsService;
import com.renjia.service.module.employee.AgreementSigningService;
import com.renjia.service.module.employeeaddition.advisorimpl.EmployeeAdvisorTypeEnum;
import com.renjia.service.module.employeeaddition.advisorimpl.common.AbstractEmployeeAdvisorImpl;
import com.renjia.service.module.employeeaddition.advisorimpl.common.EmployeeAutoCompleteModifier;
import com.renjia.service.module.employeeaddition.advisorimpl.payroll.payload.PayrollEmployeeBatchPayload;
import com.renjia.service.module.employeeaddition.advisorimpl.payroll.payload.PayrollEmployeePiecePayload;
import com.renjia.service.module.employeeaddition.advisorimpl.payroll.payload.PayrollEmployeeSinglePayload;
import com.renjia.service.module.messagecenter.MsgCenterSendService;
import com.renjia.service.module.payroll.advisor.verify.*;
import com.renjia.util.EpayCallingService;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * 费用发放人员导入实现
 *
 * @author zhang xiao bin
 * @version v1.0
 * @date 2021/03/03
 * @since v5.0.6
 **/
@Service
public class PayrollEmployeeAdvisorImpl extends
        AbstractEmployeeAdvisorImpl<BusPayrollRecord, BusPayrollRecordDetail, PayrollEmployeeBatchPayload, PayrollEmployeePiecePayload, PayrollEmployeeSinglePayload>
        implements
        EmployeeAutoCompleteModifier<BusPayrollRecord, BusPayrollRecordDetail, PayrollEmployeeBatchPayload, PayrollEmployeePiecePayload, PayrollEmployeeSinglePayload, PayrollEmployeeAdvisorImpl> {

    protected final PayrollRecordSupportService payrollRecordSupportService;
    protected final PayrollDetailSupportService payrollDetailSupportService;
    protected final RedisTemplate<String, Object> redisTemplate;
    protected final BusPayrollRecordMapper busPayrollRecordMapper;
    protected final BusPayrollRecordDetailMapper busPayrollRecordDetailMapper;
    protected final PayrollVerifyBatchStartAdvisor payrollVerifyBatchStartAdvisor;
    protected final PayrollVerifySingleValidateAdvisor payrollVerifySingleValidateAdvisor;
    protected final PayrollVerifySingleValidateEmployeeAdvisor payrollVerifySingleValidateEmployeeAdvisor;
    protected final PayrollVerifySingleProcessAdvisor payrollVerifySingleProcessAdvisor;
    protected final PayrollVerifySingleSuccessAdvisor payrollVerifySingleSuccessAdvisor;
    protected final PayrollVerifySingleFailedAdvisor payrollVerifySingleFailedAdvisor;
    protected final PayrollVerifySinglePostAdvisor payrollVerifySinglePostAdvisor;
    protected final PayrollVerifyPiecePostAdvisor payrollVerifyPiecePostAdvisor;
    protected final MsgCenterSendService msgCenterSendService;
    protected final UkerServerProperties ukerServerProperties;

    public PayrollEmployeeAdvisorImpl(CoreEmployeeImportBatchService coreEmployeeImportBatchService, CoreEmployeeMapper coreEmployeeMapper,
                                      CoreEmployeeCustomerMapper coreEmployeeCustomerMapper, CoreUserBankMapper coreUserBankMapper,
                                      CoreAgreementDetailMapper coreAgreementDetailMapper, ThreeElementsService threeElementsService, EmployeeSupportService employeeSupportService,
                                      EmployeeCustomerSupportService employeeCustomerSupportService, BankCardSupportService bankCardSupportService,
                                      CustomerChannelSupportService customerChannelSupportService, AgreementSigningService agreementSigningService, EpayCallingService epayCallingService, LogService logService,
                                      PayrollRecordSupportService payrollRecordSupportService, PayrollDetailSupportService payrollDetailSupportService,
                                      RedisTemplate<String, Object> redisTemplate, BusPayrollRecordMapper busPayrollRecordMapper,
                                      BusPayrollRecordDetailMapper busPayrollRecordDetailMapper, PayrollVerifyBatchStartAdvisor payrollVerifyBatchStartAdvisor,
                                      PayrollVerifySingleValidateAdvisor payrollVerifySingleValidateAdvisor,
                                      PayrollVerifySingleValidateEmployeeAdvisor payrollVerifySingleValidateEmployeeAdvisor,
                                      PayrollVerifySingleProcessAdvisor payrollVerifySingleProcessAdvisor, PayrollVerifySingleSuccessAdvisor payrollVerifySingleSuccessAdvisor,
                                      PayrollVerifySingleFailedAdvisor payrollVerifySingleFailedAdvisor, PayrollVerifySinglePostAdvisor payrollVerifySinglePostAdvisor,
                                      PayrollVerifyPiecePostAdvisor payrollVerifyPiecePostAdvisor, MsgCenterSendService msgCenterSendService,
                                      UkerServerProperties ukerServerProperties) {
        super(coreEmployeeImportBatchService, coreEmployeeMapper, coreEmployeeCustomerMapper, coreUserBankMapper, coreAgreementDetailMapper, threeElementsService,
                employeeSupportService, employeeCustomerSupportService, bankCardSupportService, customerChannelSupportService, agreementSigningService, epayCallingService, logService);
        this.payrollRecordSupportService = payrollRecordSupportService;
        this.payrollDetailSupportService = payrollDetailSupportService;
        this.redisTemplate = redisTemplate;
        this.busPayrollRecordMapper = busPayrollRecordMapper;
        this.busPayrollRecordDetailMapper = busPayrollRecordDetailMapper;
        this.payrollVerifyBatchStartAdvisor = payrollVerifyBatchStartAdvisor;
        this.payrollVerifySingleValidateAdvisor = payrollVerifySingleValidateAdvisor;
        this.payrollVerifySingleValidateEmployeeAdvisor = payrollVerifySingleValidateEmployeeAdvisor;
        this.payrollVerifySingleProcessAdvisor = payrollVerifySingleProcessAdvisor;
        this.payrollVerifySingleSuccessAdvisor = payrollVerifySingleSuccessAdvisor;
        this.payrollVerifySingleFailedAdvisor = payrollVerifySingleFailedAdvisor;
        this.payrollVerifySinglePostAdvisor = payrollVerifySinglePostAdvisor;
        this.payrollVerifyPiecePostAdvisor = payrollVerifyPiecePostAdvisor;
        this.msgCenterSendService = msgCenterSendService;
        this.ukerServerProperties = ukerServerProperties;
    }

    @Override
    public boolean shallProcess(@NonNull AbstractBatchPayload<CoreEmployeeImportBatch, EmployeeAdvisorTypeEnum, BusPayrollRecord, BusPayrollRecordDetail> advisorPayload) {
        return EmployeeAdvisorTypeEnum.PAYROLL_RECORD == advisorPayload.getAdvisorTypeEnum();
    }

    /**
     * 执行人员导入的批次准备后，还要更新发放批次的状态，初始化 redis 统计缓存，最后执行其他发放扩展的批次启动的扩展实现
     *
     * @param batchPayload 批次数据
     * @return true 或者抛出异常
     */
    @Override
    public boolean doPrepareOnBatchStart(@NonNull PayrollEmployeeBatchPayload batchPayload) {
        if (super.doPrepareOnBatchStart(batchPayload)
                && payrollRecordSupportService.updateValidStatus(batchPayload.getBatchEntity(), PayrollValidateStatusEnum.WAITING, PayrollValidateStatusEnum.PROCESSING)) {
            //4.8.6使用redis
            Map<String, Integer> map = new HashMap<>(4);
            map.put(Constant.TOTAL, batchPayload.getSingleEntityList().size());
            map.put(Constant.VALID_FAIL_COUNT, 0);
            map.put(Constant.VALID_PASS_COUNT, Constant.COMMON_ZERO);
            map.put(Constant.SURPLUS, batchPayload.getSingleEntityList().size());
            redisTemplate.opsForHash().putAll(Constant.VALIDATE + "_" + batchPayload.getBatchEntity().getId(), map);
            //设置时间为60分钟
            redisTemplate.expire(Constant.VALIDATE + "_" + batchPayload.getBatchEntity().getId(), ukerServerProperties.getPayrollRedisOverTimeHours(), TimeUnit.HOURS);

            return payrollVerifyBatchStartAdvisor.doPrepareOnBatchStartAtVerifyStage(batchPayload);
        }
        return false;
    }

    @Override
    public boolean doValidateBeforeSingleProcess(@NonNull PayrollEmployeeSinglePayload singlePayload, @NonNull PayrollEmployeePiecePayload piecePayload,
                                                 @NonNull PayrollEmployeeBatchPayload batchPayload) {
        // 先检查是否执行自动补全；如果取消预加载 EmployeeAdditionPreloadAdvisorImpl，则此处就得先根据证件信息查询用户
        checkAutoComplete(singlePayload, piecePayload, batchPayload, this);
        if (!isAgeValidated(singlePayload, batchPayload)) {
            return false;
        }
        // 完成人员参数的校验，执行发放相关参数的校验
        return super.doValidateBeforeSingleProcess(singlePayload, piecePayload, batchPayload)
                && payrollVerifySingleValidateAdvisor.doValidateBeforeFindEmployeeAtVerifyStage(singlePayload, piecePayload, batchPayload);
    }

    /**
     * 人员导入处理完 CoreEmployee 后，新用户的话已保存 CoreEmployee，但未生成其他对象
     * 执行费用发放的校验，比如发放配置 人员已存在 的校验
     *
     * @param singlePayload sp
     * @param piecePayload  pp
     * @param batchPayload  bp
     * @return true 表示人员处理成功，且发放校验通过
     */
    @Override
    protected boolean dealWithCoreEmployee(PayrollEmployeeSinglePayload singlePayload, PayrollEmployeePiecePayload piecePayload, PayrollEmployeeBatchPayload batchPayload) {
        // 已经根据证件信息或手机号查询用户，但是尚未检查和处理
        return super.dealWithCoreEmployee(singlePayload, piecePayload, batchPayload)
                // 用户不存在时 employee 已入库，用户已存在时，已经查询过 employeeCustomer 和 coreUserBank
                && payrollVerifySingleValidateEmployeeAdvisor.doValidateAfterDealWithEmployeeAtVerifyStage(singlePayload, piecePayload, batchPayload);
    }

    /**
     * 人员验证导入成功后，执行费用发放的验证，比如计算月限额
     *
     * @param singlePayload 单条数据
     * @param piecePayload  分片数据
     * @param batchPayload  批次数据
     * @return true 表示人员导入成功，且费用发放的校验通过
     */
    @Override
    public boolean doExecuteOnSingleProcess(@NonNull PayrollEmployeeSinglePayload singlePayload, @NonNull PayrollEmployeePiecePayload piecePayload,
                                            @NonNull PayrollEmployeeBatchPayload batchPayload) {
        return super.doExecuteOnSingleProcess(singlePayload, piecePayload, batchPayload)
                && payrollVerifySingleProcessAdvisor.doExecuteOnEmployeeInfoSavedAtVerifyStage(singlePayload, piecePayload, batchPayload);
    }

    @Override
    public PayrollEmployeePiecePayload packPiecePayload(List<PayrollEmployeeSinglePayload> singlePayloadList) {
        return new PayrollEmployeePiecePayload(singlePayloadList);
    }

    @Override
    public PayrollEmployeeSinglePayload packSinglePayload(BusPayrollRecordDetail singleEntity) {
        return new PayrollEmployeeSinglePayload(singleEntity);
    }

    @Override
    protected String getDocumentTypeAndNumberString(BusPayrollRecordDetail singleEntity) {
        return singleEntity.getDocumentType() + "-" + singleEntity.getDocumentNumber();
    }

    @Override
    protected boolean isSingleEntityProcessed(PayrollEmployeeSinglePayload singlePayload) {
        return !PayrollDetailValidateStatusEnum.VALIDATING.getCode().equals(singlePayload.getSingleEntity().getValidStatus());
    }

    @Override
    public void autoCompleteSingleEntity(@NonNull PayrollEmployeeSinglePayload singlePayload, @NonNull String bankCard, @NonNull String mobile,
                                         @NonNull AutoCompleteFlagParser autoCompleteFlagParser) {
        singlePayload.getSingleEntity().setRemark0(bankCard);
        singlePayload.getSingleEntity().setMobile(mobile);
        singlePayload.getSingleEntity().setAutoCompleteFlag(autoCompleteFlagParser.getAutoCompleteFlag());
    }

    @Override
    public boolean doSettleOnSingleProcessed(@NonNull PayrollEmployeeSinglePayload singlePayload, @NonNull PayrollEmployeePiecePayload piecePayload,
                                             @NonNull PayrollEmployeeBatchPayload batchPayload) {
        super.doSettleOnSingleProcessed(singlePayload, piecePayload, batchPayload);
        singlePayload.getSingleEntity().setUpdateUser(batchPayload.getCoreUser());
        busPayrollRecordDetailMapper.updateById(singlePayload.getSingleEntity());
        if (singlePayload.getErrMsgList().isEmpty()) {
            // 发放验证通过
            redisTemplate.opsForHash().increment(Constant.VALIDATE + "_" + batchPayload.getBatchEntity().getId(), Constant.VALID_PASS_COUNT, 1);
        } else {
            redisTemplate.opsForHash().increment(Constant.VALIDATE + "_" + batchPayload.getBatchEntity().getId(), Constant.VALID_FAIL_COUNT, 1);
        }
        redisTemplate.opsForHash().increment(Constant.VALIDATE + "_" + batchPayload.getBatchEntity().getId(), Constant.SURPLUS, -1);
        return true;
    }

    @Override
    protected void updateValidateSuccess(PayrollEmployeeSinglePayload singlePayload, PayrollEmployeePiecePayload piecePayload, PayrollEmployeeBatchPayload batchPayload) {
        singlePayload.getSingleEntity().setEmployeeId(singlePayload.getCoreEmployee().getId());
        payrollVerifySingleSuccessAdvisor.doSettleOnPayrollDetailValidateSuccessAtVerifyStage(singlePayload, piecePayload, batchPayload);
    }

    @Override
    protected void updateValidateFailed(PayrollEmployeeSinglePayload singlePayload, PayrollEmployeePiecePayload piecePayload, PayrollEmployeeBatchPayload batchPayload,
                                        String errMsg) {
        if (singlePayload.getCoreEmployee() != null) {
            singlePayload.getSingleEntity().setEmployeeId(singlePayload.getCoreEmployee().getId());
        }
        payrollVerifySingleFailedAdvisor.doRecoverOnPayrollDetailValidateFailedAtVerifyStage(singlePayload, piecePayload, batchPayload, errMsg);
    }

    @Override
    public boolean doCleanUpOnSinglePost(@NonNull PayrollEmployeeSinglePayload singlePayload, @NonNull PayrollEmployeePiecePayload piecePayload,
                                         @NonNull PayrollEmployeeBatchPayload batchPayload) {
        return payrollVerifySinglePostAdvisor.doCleanUpOnPayrollDetailProcessPostAtVerifyStage(singlePayload, piecePayload, batchPayload);
    }

    @Override
    public boolean doCleanUpOnPiecePost(@NonNull PayrollEmployeePiecePayload piecePayload, @NonNull PayrollEmployeeBatchPayload batchPayload) {
        return payrollVerifyPiecePostAdvisor.doCleanUpOnPayrollDetailPieceProcessPostAtVerifyStage(piecePayload, batchPayload);
    }

    @Override
    public boolean doSettleOnBatchProcessed(@NonNull PayrollEmployeeBatchPayload batchPayload) {
        super.doSettleOnBatchProcessed(batchPayload);
        batchPayload.getBatchEntity().setValidStatus(PayrollValidateStatusEnum.AFFIRM.getCode());
        batchPayload.getBatchEntity().setValidFinishTime(new Date());
        busPayrollRecordMapper.updateById(batchPayload.getBatchEntity());
        return true;
    }

    @Override
    public boolean doRecoverOnBatchException(@NonNull PayrollEmployeeBatchPayload batchPayload, String errorMessage) {
        super.doRecoverOnBatchException(batchPayload, errorMessage);
        if (payrollRecordSupportService.updateValidStatus(batchPayload.getBatchEntity(), PayrollValidateStatusEnum.PROCESSING, PayrollValidateStatusEnum.WAITING)) {
            // 重置批次状态，准备重试
        }
        return true;
    }

    @Override
    public boolean doCleanUpOnBatchPost(@NonNull PayrollEmployeeBatchPayload batchPayload) {
        super.doCleanUpOnBatchPost(batchPayload);
        redisTemplate.opsForHash().delete(Constant.VALIDATE + "_" + batchPayload.getBatchEntity().getId(), Constant.TOTAL, Constant.VALID_FAIL_COUNT, Constant.VALID_PASS_COUNT,
                Constant.SURPLUS);
        // 验证完成，发送通知
        msgCenterSendService.payrollRecordVerifyFinishedNotify(batchPayload);
        return true;
    }

    @Override
    public boolean allowEmployeeRepeat(PayrollEmployeeSinglePayload singlePayload, PayrollEmployeePiecePayload piecePayload, PayrollEmployeeBatchPayload batchPayload) {
        return SwitchEnum.OFF.getCode2().equals(batchPayload.getCoreCustomerChannelMerchant().getAllowEmployeeRepeat());
    }
}
